#include "wx/wxprec.h"
#include "wx/xrc/xmlres.h"
#include <locale.h>
#include "wx/wfstream.h"
#include "wx/filesys.h"
#include "wx/filename.h"
#include "wx/tokenzr.h"
#include "wx/fontenum.h"
#include "wx/fontmap.h"
#include "wx/artprov.h"
#include "wx/xml/xml.h"
#include "wx/arrimpl.cpp"

WX_DEFINE_OBJARRAY( wxXmlResourceDataRecords )
wxXmlResource *wxXmlResource::ms_instance = NULL;

wxXmlResource *wxXmlResource::Get() {
  if( !ms_instance ) {
    ms_instance = new wxXmlResource;
  }
  return ms_instance;
}

wxXmlResource *wxXmlResource::Set( wxXmlResource *res ) {
  wxXmlResource *old = ms_instance;
  ms_instance = res;
  return old;
}

wxXmlResource::wxXmlResource( int flags, const wxString& domain ) {
  m_flags = flags;
  m_domain = NULL;
  if( ! domain.empty() ) {
    SetDomain( domain );
  }
}

wxXmlResource::wxXmlResource( const wxString& filemask, int flags, const wxString& domain ) {
  m_flags = flags;
  m_domain = NULL;
  if( ! domain.empty() ) {
    SetDomain( domain );
  }
  Load( filemask );
}

wxXmlResource::~wxXmlResource() {
  if( m_domain ) {
    free( m_domain );
  }
  ClearHandlers();
}

void wxXmlResource::SetDomain( const wxChar* domain ) {
  if( m_domain ) {
    free( m_domain );
  }
  m_domain = NULL;
  if( domain && wxStrlen( domain ) ) {
    m_domain = wxStrdup( domain );
  }
}

wxString wxXmlResource::ConvertFileNameToURL( const wxString& filename ) {
  wxString fnd( filename );
  if( wxFileName::FileExists( fnd ) ) {
    wxFileName fn( fnd );
    if( fn.IsRelative() ) {
      fn.MakeAbsolute();
      fnd = fn.GetFullPath();
    }
    fnd = wxFileSystem::FileNameToURL( fnd );
  }
  return fnd;
}

bool wxXmlResource::IsArchive( const wxString& filename ) {
  const wxString fnd = filename.Lower();
  return fnd.Matches( wxT( "*.zip" ) ) || fnd.Matches( wxT( "*.xrs" ) );
}

bool wxXmlResource::Load( const wxString& filemask ) {  //加载资源文件
  wxString fnd;
  wxXmlResourceDataRecord *drec;
  bool iswild = wxIsWild( filemask );
  bool rt = true;
  wxFileSystem fsys;
  if( iswild ) {
    fnd = fsys.FindFirst( filemask, wxFILE );
  } else {
    fnd = filemask;
  }
  while( !fnd.empty() ) {
    fnd = ConvertFileNameToURL( fnd );
    if( IsArchive( fnd ) ) {
      rt = rt && Load( fnd + wxT( "#zip:*.xrc" ) );
    } else {
      drec = new wxXmlResourceDataRecord;
      drec->File = fnd;
      m_data.Add( drec );
    }
    if( iswild ) {
      fnd = fsys.FindNext();
    } else {
      fnd = wxEmptyString;
    }
  }
  return rt && UpdateResources();
}

bool wxXmlResource::Unload( const wxString& filename ) {
  wxASSERT_MSG( !wxIsWild( filename ), _T( "wildcards not supported by wxXmlResource::Unload()" ) );
  wxString fnd = ConvertFileNameToURL( filename );
  const bool isArchive = IsArchive( fnd );
  if( isArchive ) {
    fnd += _T( "#zip:" );
  }
  bool unloaded = false;
  const size_t count = m_data.GetCount();
  for( size_t i = 0; i < count; i++ ) {
    if( isArchive ) {
      if( m_data[i].File.StartsWith( fnd ) ) {
        unloaded = true;
      }
    } else {
      if( m_data[i].File == fnd ) {
        m_data.RemoveAt( i );
        unloaded = true;
        break;
      }
    }
  }
  return unloaded;
}

IMPLEMENT_ABSTRACT_CLASS( wxXmlResourceHandler, wxObject )

void wxXmlResource::AddHandler( wxXmlResourceHandler *handler ) {
  m_handlers.Append( handler );
  handler->SetParentResource( this );
}

void wxXmlResource::InsertHandler( wxXmlResourceHandler *handler ) {
  m_handlers.Insert( handler );
  handler->SetParentResource( this );
}

void wxXmlResource::ClearHandlers() {
  WX_CLEAR_LIST( wxList, m_handlers );
}

wxMenu *wxXmlResource::LoadMenu( const wxString& name ) {
  return ( wxMenu* )CreateResFromNode( FindResource( name, wxT( "wxMenu" ) ), NULL, NULL );
}

wxMenuBar *wxXmlResource::LoadMenuBar( wxWindow *parent, const wxString& name ) {
  return ( wxMenuBar* )CreateResFromNode( FindResource( name, wxT( "wxMenuBar" ) ), parent, NULL );
}

wxToolBar *wxXmlResource::LoadToolBar( wxWindow *parent, const wxString& name ) {
  return ( wxToolBar* )CreateResFromNode( FindResource( name, wxT( "wxToolBar" ) ), parent, NULL );
}

wxDialog *wxXmlResource::LoadDialog( wxWindow *parent, const wxString& name ) {
  return ( wxDialog* )CreateResFromNode( FindResource( name, wxT( "wxDialog" ) ), parent, NULL );
}

bool wxXmlResource::LoadDialog( wxDialog *dlg, wxWindow *parent, const wxString& name ) {
  return CreateResFromNode( FindResource( name, wxT( "wxDialog" ) ), parent, dlg ) != NULL;
}

wxPanel *wxXmlResource::LoadPanel( wxWindow *parent, const wxString& name ) {
  return ( wxPanel* )CreateResFromNode( FindResource( name, wxT( "wxPanel" ) ), parent, NULL );
}

bool wxXmlResource::LoadPanel( wxPanel *panel, wxWindow *parent, const wxString& name ) {
  return CreateResFromNode( FindResource( name, wxT( "wxPanel" ) ), parent, panel ) != NULL;
}

wxFrame *wxXmlResource::LoadFrame( wxWindow* parent, const wxString& name ) {
  return ( wxFrame* )CreateResFromNode( FindResource( name, wxT( "wxFrame" ) ), parent, NULL );
}

bool wxXmlResource::LoadFrame( wxFrame* frame, wxWindow *parent, const wxString& name ) {
  return CreateResFromNode( FindResource( name, wxT( "wxFrame" ) ), parent, frame ) != NULL;
}

wxBitmap wxXmlResource::LoadBitmap( const wxString& name ) {
  wxBitmap *bmp = ( wxBitmap* )CreateResFromNode( FindResource( name, wxT( "wxBitmap" ) ), NULL, NULL );
  wxBitmap rt;
  if( bmp ) {
    rt = *bmp;
    delete bmp;
  }
  return rt;
}

wxIcon wxXmlResource::LoadIcon( const wxString& name ) {
  wxIcon *icon = ( wxIcon* )CreateResFromNode( FindResource( name, wxT( "wxIcon" ) ), NULL, NULL );
  wxIcon rt;
  if( icon ) {
    rt = *icon;
    delete icon;
  }
  return rt;
}

wxObject *wxXmlResource::LoadObject( wxWindow *parent, const wxString& name, const wxString& classname ) {
  return CreateResFromNode( FindResource( name, classname ), parent, NULL );
}

bool wxXmlResource::LoadObject( wxObject *instance, wxWindow *parent, const wxString& name, const wxString& classname ) {
  return CreateResFromNode( FindResource( name, classname ), parent, instance ) != NULL;
}

bool wxXmlResource::AttachUnknownControl( const wxString& name,
    wxWindow *control, wxWindow *parent ) {
  if( parent == NULL ) {
    parent = control->GetParent();
  }
  wxWindow *container = parent->FindWindow( name + wxT( "_container" ) );
  if( !container ) {
    wxLogError( _( "找不到未知控件的容器 '%s'." ), name.c_str() );
    return false;
  }
  return control->Reparent( container );
}

static void ProcessPlatformProperty( wxXmlNode *node ) {
  wxString s;
  bool isok;
  wxXmlNode *c = node->GetChildren();
  while( c ) {
    isok = false;
    if( !c->GetPropVal( wxT( "platform" ), &s ) ) {
      isok = true;
    } else {
      wxStringTokenizer tkn( s, wxT( " |" ) );
      while( tkn.HasMoreTokens() ) {
        s = tkn.GetNextToken();
        if( s == wxT( "unix" ) ) {
          isok = true;
        }
        if( isok ) {
          break;
        }
      }
    }
    if( isok ) {
      ProcessPlatformProperty( c );
      c = c->GetNext();
    } else {
      wxXmlNode *c2 = c->GetNext();
      node->RemoveChild( c );
      delete c;
      c = c2;
    }
  }
}

bool wxXmlResource::UpdateResources() {
  bool rt = true;
  bool modif;
  wxFSFile *file = NULL;
  wxUnusedVar( file );
  wxFileSystem fsys;
  wxString encoding( wxT( "UTF-8" ) );
  for( size_t i = 0; i < m_data.GetCount(); i++ ) {
    modif = ( m_data[i].Doc == NULL );
    if( !modif && !( m_flags & wxXRC_NO_RELOADING ) ) {
      file = fsys.OpenFile( m_data[i].File );
      modif = file && file->GetModificationTime() > m_data[i].Time;
      if( !file ) {
        wxLogInfo( _( "无法打开文件 '%s'." ), m_data[i].File.c_str() );
        rt = false;
      }
      wxDELETE( file );
      wxUnusedVar( file );
    }
    if( modif ) {
      wxLogTrace( _T( "xrc" ), _T( "打开文件 '%s'" ), m_data[i].File.c_str() );
      wxInputStream *stream = NULL;
      file = fsys.OpenFile( m_data[i].File );
      if( file ) {
        stream = file->GetStream();
      }
      if( stream ) {
        delete m_data[i].Doc;
        m_data[i].Doc = new wxXmlDocument;
      }
      if( !stream || !m_data[i].Doc->Load( *stream, encoding ) ) {
        wxLogError( _( "无法从文件加载资源 '%s'." ), m_data[i].File.c_str() );
        wxDELETE( m_data[i].Doc );
        rt = false;
      } else if( m_data[i].Doc->GetRoot()->GetName() != wxT( "resource" ) ) {
        wxLogError( _( "无效的XRC资源 '%s': 没有根节点资源'." ), m_data[i].File.c_str() );
        wxDELETE( m_data[i].Doc );
        rt = false;
      } else {
        ProcessPlatformProperty( m_data[i].Doc->GetRoot() );
        m_data[i].Time = file->GetModificationTime();
      }
      wxDELETE( file );
      wxUnusedVar( file );
    }
  }
  return rt;
}

wxXmlNode *wxXmlResource::DoFindResource( wxXmlNode *parent, const wxString& name, const wxString& classname, bool recursive ) {
  wxString dummy;
  wxXmlNode *node;
  for( node = parent->GetChildren(); node; node = node->GetNext() ) {
    if( node->GetType() == wxXML_ELEMENT_NODE && ( node->GetName() == wxT( "object" ) ||
        node->GetName() == wxT( "object_ref" ) ) &&
        node->GetPropVal( wxT( "name" ), &dummy ) && dummy == name ) {
      wxString cls( node->GetPropVal( wxT( "class" ), wxEmptyString ) );
      if( !classname || cls == classname ) {
        return node;
      }
      if( cls.empty() && node->GetName() == wxT( "object_ref" ) ) {
        wxString refName = node->GetPropVal( wxT( "ref" ), wxEmptyString );
        if( refName.empty() ) {
          continue;
        }
        wxXmlNode* refNode = FindResource( refName, wxEmptyString, true );
        if( refNode &&
            refNode->GetPropVal( wxT( "class" ), wxEmptyString ) == classname ) {
          return node;
        }
      }
    }
  }
  if( recursive )
    for( node = parent->GetChildren(); node; node = node->GetNext() ) {
      if( node->GetType() == wxXML_ELEMENT_NODE && ( node->GetName() == wxT( "object" ) || node->GetName() == wxT( "object_ref" ) ) ) {
        wxXmlNode* found = DoFindResource( node, name, classname, true );
        if( found ) {
          return found;
        }
      }
    }
  return NULL;
}

wxXmlNode *wxXmlResource::FindResource( const wxString& name, const wxString& classname, bool recursive ) {
  UpdateResources();
  wxString dummy;
  for( size_t f = 0; f < m_data.GetCount(); f++ ) {
    if( m_data[f].Doc == NULL || m_data[f].Doc->GetRoot() == NULL ) {
      continue;
    }
    wxXmlNode* found = DoFindResource( m_data[f].Doc->GetRoot(), name, classname, recursive );
    if( found ) {
      m_curFileSystem.ChangePathTo( m_data[f].File );
      return found;
    }
  }
  wxLogError( _( "XRC resource '%s' (class '%s') not found!" ), name.c_str(), classname.c_str() );
  return NULL;
}

static void MergeNodes( wxXmlNode& dest, wxXmlNode& with ) {
  for( wxXmlProperty *prop = with.GetProperties(); prop; prop = prop->GetNext() ) {
    wxXmlProperty *dprop;
    for( dprop = dest.GetProperties(); dprop; dprop = dprop->GetNext() ) {
      if( dprop->GetName() == prop->GetName() ) {
        dprop->SetValue( prop->GetValue() );
        break;
      }
    }
    if( !dprop ) {
      dest.AddProperty( prop->GetName(), prop->GetValue() );
    }
  }
  for( wxXmlNode* node = with.GetChildren(); node; node = node->GetNext() ) {
    wxString name = node->GetPropVal( wxT( "name" ), wxEmptyString );
    wxXmlNode *dnode;
    for( dnode = dest.GetChildren(); dnode; dnode = dnode->GetNext() ) {
      if( dnode->GetName() == node->GetName() &&
          dnode->GetPropVal( wxT( "name" ), wxEmptyString ) == name &&
          dnode->GetType() == node->GetType() ) {
        MergeNodes( *dnode, *node );
        break;
      }
    }
    if( !dnode ) {
      static const wxChar *AT_END = wxT( "end" );
      wxString insert_pos = node->GetPropVal( wxT( "insert_at" ), AT_END );
      if( insert_pos == AT_END ) {
        dest.AddChild( new wxXmlNode( *node ) );
      } else if( insert_pos == wxT( "begin" ) ) {
        dest.InsertChild( new wxXmlNode( *node ), dest.GetChildren() );
      }
    }
  }
  if( dest.GetType() == wxXML_TEXT_NODE && with.GetContent().length() ) {
    dest.SetContent( with.GetContent() );
  }
}

wxObject *wxXmlResource::CreateResFromNode( wxXmlNode *node, wxObject *parent,
    wxObject *instance,
    wxXmlResourceHandler *handlerToUse ) {
  if( node == NULL ) {
    return NULL;
  }
  if( node->GetName() == wxT( "object_ref" ) ) {
    wxString refName = node->GetPropVal( wxT( "ref" ), wxEmptyString );
    wxXmlNode* refNode = FindResource( refName, wxEmptyString, true );
    if( !refNode ) {
      wxLogError( _( "带有ref的引用对象节点=\"%s\" 找不到!" ), refName.c_str() );
      return NULL;
    }
    wxXmlNode copy( *refNode );
    MergeNodes( copy, *node );
    return CreateResFromNode( &copy, parent, instance );
  }
  wxXmlResourceHandler *handler;
  if( handlerToUse ) {
    if( handlerToUse->CanHandle( node ) ) {
      return handlerToUse->CreateResource( node, parent, instance );
    }
  } else if( node->GetName() == wxT( "object" ) ) {
    wxList::compatibility_iterator ND = m_handlers.GetFirst();
    while( ND ) {
      handler = ( wxXmlResourceHandler* )ND->GetData();
      if( handler->CanHandle( node ) ) {
        return handler->CreateResource( node, parent, instance );
      }
      ND = ND->GetNext();
    }
  }
  wxLogError( _( "找不到XML节点的处理程序 '%s', 类 '%s'!" ), node->GetName().c_str(),
              node->GetPropVal( wxT( "class" ), wxEmptyString ).c_str() );
  return NULL;
}

#include "wx/listimpl.cpp"
WX_DECLARE_LIST( wxXmlSubclassFactory, wxXmlSubclassFactoriesList );
WX_DEFINE_LIST( wxXmlSubclassFactoriesList )

wxXmlSubclassFactoriesList *wxXmlResource::ms_subclassFactories = NULL;

void wxXmlResource::AddSubclassFactory( wxXmlSubclassFactory *factory ) {
  if( !ms_subclassFactories ) {
    ms_subclassFactories = new wxXmlSubclassFactoriesList;
  }
  ms_subclassFactories->Append( factory );
}

class wxXmlSubclassFactoryCXX : public wxXmlSubclassFactory {
  public:
    ~wxXmlSubclassFactoryCXX() {}
    wxObject *Create( const wxString& className ) {
      wxClassInfo* classInfo = wxClassInfo::FindClass( className );
      if( classInfo ) {
        return classInfo->CreateObject();
      } else {
        return NULL;
      }
    }
};

wxXmlResourceHandler::wxXmlResourceHandler()
  : m_node( NULL ), m_parent( NULL ), m_instance( NULL ), m_parentAsWindow( NULL ) {}

wxObject *wxXmlResourceHandler::CreateResource( wxXmlNode *node, wxObject *parent, wxObject *instance ) {
  wxXmlNode *myNode = m_node;
  wxString myClass = m_class;
  wxObject *myParent = m_parent, *myInstance = m_instance;
  wxWindow *myParentAW = m_parentAsWindow;
  m_instance = instance;
  if( !m_instance && node->HasProp( wxT( "subclass" ) ) &&
      !( m_resource->GetFlags() & wxXRC_NO_SUBCLASSING ) ) {
    wxString subclass = node->GetPropVal( wxT( "subclass" ), wxEmptyString );
    if( !subclass.empty() ) {
      for( wxXmlSubclassFactoriesList::compatibility_iterator i = wxXmlResource::ms_subclassFactories->GetFirst();
           i; i = i->GetNext() ) {
        m_instance = i->GetData()->Create( subclass );
        if( m_instance ) {
          break;
        }
      }
      if( !m_instance ) {
        wxString name = node->GetPropVal( wxT( "name" ), wxEmptyString );
        wxLogError( _( "Subclass '%s' 找不到资源 '%s', not subclassing!" ),
                    subclass.c_str(), name.c_str() );
      }
    }
  }
  m_node = node;
  m_class = node->GetPropVal( wxT( "class" ), wxEmptyString );
  m_parent = parent;
  m_parentAsWindow = wxDynamicCast( m_parent, wxWindow );
  wxObject *returned = DoCreateResource();
  m_node = myNode;
  m_class = myClass;
  m_parent = myParent;
  m_parentAsWindow = myParentAW;
  m_instance = myInstance;
  return returned;
}

void wxXmlResourceHandler::AddStyle( const wxString& name, int value ) {
  m_styleNames.Add( name );
  m_styleValues.Add( value );
}

void wxXmlResourceHandler::AddWindowStyles() {
  XRC_ADD_STYLE( wxCLIP_CHILDREN );
  XRC_ADD_STYLE( wxSIMPLE_BORDER );
  XRC_ADD_STYLE( wxBORDER_SIMPLE );
  XRC_ADD_STYLE( wxSUNKEN_BORDER );
  XRC_ADD_STYLE( wxBORDER_SUNKEN );
  XRC_ADD_STYLE( wxDOUBLE_BORDER );
  XRC_ADD_STYLE( wxBORDER_DOUBLE );
  XRC_ADD_STYLE( wxBORDER_THEME );
  XRC_ADD_STYLE( wxRAISED_BORDER );
  XRC_ADD_STYLE( wxBORDER_RAISED );
  XRC_ADD_STYLE( wxSTATIC_BORDER );
  XRC_ADD_STYLE( wxBORDER_STATIC );
  XRC_ADD_STYLE( wxNO_BORDER );
  XRC_ADD_STYLE( wxBORDER_NONE );
  XRC_ADD_STYLE( wxTRANSPARENT_WINDOW );
  XRC_ADD_STYLE( wxWANTS_CHARS );
  XRC_ADD_STYLE( wxTAB_TRAVERSAL );
  XRC_ADD_STYLE( wxNO_FULL_REPAINT_ON_RESIZE );
  XRC_ADD_STYLE( wxFULL_REPAINT_ON_RESIZE );
  XRC_ADD_STYLE( wxALWAYS_SHOW_SB );
  XRC_ADD_STYLE( wxWS_EX_BLOCK_EVENTS );
  XRC_ADD_STYLE( wxWS_EX_VALIDATE_RECURSIVELY );
}

bool wxXmlResourceHandler::HasParam( const wxString& param ) {
  return ( GetParamNode( param ) != NULL );
}

int wxXmlResourceHandler::GetStyle( const wxString& param, int defaults ) {
  wxString s = GetParamValue( param );
  if( !s ) {
    return defaults;
  }
  wxStringTokenizer tkn( s, wxT( "| \t\n" ), wxTOKEN_STRTOK );
  int style = 0;
  int index;
  wxString fl;
  while( tkn.HasMoreTokens() ) {
    fl = tkn.GetNextToken();
    index = m_styleNames.Index( fl );
    if( index != wxNOT_FOUND ) {
      style |= m_styleValues[index];
    } else {
      wxLogError( _( "未知样式标志 " ) + fl );
    }
  }
  return style;
}

wxString wxXmlResourceHandler::GetText( const wxString& param, bool translate ) {
  wxXmlNode *parNode = GetParamNode( param );
  wxString str1( GetNodeContent( parNode ) );
  wxString str2;
  const wxChar *dt;
  wxChar amp_char;
  amp_char = wxT( '_' );
  for( dt = str1.c_str(); *dt; dt++ ) {
    if( *dt == amp_char ) {
      if( *( ++dt ) == amp_char ) {
        str2 << amp_char;
      } else
      { str2 << wxT( '&' ) << *dt; }
    } else if( *dt == wxT( '\\' ) )
      switch( *( ++dt ) ) {
        case wxT( 'n' ):
          str2 << wxT( '\n' );
          break;
        case wxT( 't' ):
          str2 << wxT( '\t' );
          break;
        case wxT( 'r' ):
          str2 << wxT( '\r' );
          break;
        case wxT( '\\' ) :
        default:
          str2 << wxT( '\\' ) << *dt;
          break;
      } else {
      str2 << *dt;
    }
  }
  if( m_resource->GetFlags() & wxXRC_USE_LOCALE ) {
    if( translate && parNode && parNode->GetPropVal( wxT( "translate" ), wxEmptyString ) != wxT( "0" ) ) {
      return wxGetTranslation( str2, m_resource->GetDomain() );
    } else {
      return str2;
    }
  }
  return str2;
}

long wxXmlResourceHandler::GetLong( const wxString& param, long defaultv ) {
  long value;
  wxString str1 = GetParamValue( param );
  if( !str1.ToLong( &value ) ) {
    value = defaultv;
  }
  return value;
}

float wxXmlResourceHandler::GetFloat( const wxString& param, float defaultv ) {
  wxString str = GetParamValue( param );
  str.Replace( wxT( "." ), wxLocale::GetInfo( wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER ) );
  double value;
  if( !str.ToDouble( &value ) ) {
    value = defaultv;
  }
  return wx_truncate_cast( float, value );
}


int wxXmlResourceHandler::GetID() {
  return wxXmlResource::GetXRCID( GetName() );
}

wxString wxXmlResourceHandler::GetName() {
  return m_node->GetPropVal( wxT( "name" ), wxT( "-1" ) );
}

bool wxXmlResourceHandler::GetBool( const wxString& param, bool defaultv ) {
  wxString v = GetParamValue( param );
  v.MakeLower();
  if( !v ) {
    return defaultv;
  }
  return ( v == wxT( "1" ) );
}

#define SYSCLR(clr) \
  if (name == _T(#clr)){   \
    return wxSystemSettings::GetColour(clr);   \
  }

static wxColour GetSystemColour( const wxString& name ) {
  if( !name.empty() ) {
    SYSCLR( wxSYS_COLOUR_SCROLLBAR )
    SYSCLR( wxSYS_COLOUR_BACKGROUND )
    SYSCLR( wxSYS_COLOUR_DESKTOP )
    SYSCLR( wxSYS_COLOUR_ACTIVECAPTION )
    SYSCLR( wxSYS_COLOUR_INACTIVECAPTION )
    SYSCLR( wxSYS_COLOUR_MENU )
    SYSCLR( wxSYS_COLOUR_WINDOW )
    SYSCLR( wxSYS_COLOUR_WINDOWFRAME )
    SYSCLR( wxSYS_COLOUR_MENUTEXT )
    SYSCLR( wxSYS_COLOUR_WINDOWTEXT )
    SYSCLR( wxSYS_COLOUR_CAPTIONTEXT )
    SYSCLR( wxSYS_COLOUR_ACTIVEBORDER )
    SYSCLR( wxSYS_COLOUR_INACTIVEBORDER )
    SYSCLR( wxSYS_COLOUR_APPWORKSPACE )
    SYSCLR( wxSYS_COLOUR_HIGHLIGHT )
    SYSCLR( wxSYS_COLOUR_HIGHLIGHTTEXT )
    SYSCLR( wxSYS_COLOUR_BTNFACE )
    SYSCLR( wxSYS_COLOUR_3DFACE )
    SYSCLR( wxSYS_COLOUR_BTNSHADOW )
    SYSCLR( wxSYS_COLOUR_3DSHADOW )
    SYSCLR( wxSYS_COLOUR_GRAYTEXT )
    SYSCLR( wxSYS_COLOUR_BTNTEXT )
    SYSCLR( wxSYS_COLOUR_INACTIVECAPTIONTEXT )
    SYSCLR( wxSYS_COLOUR_BTNHIGHLIGHT )
    SYSCLR( wxSYS_COLOUR_BTNHILIGHT )
    SYSCLR( wxSYS_COLOUR_3DHIGHLIGHT )
    SYSCLR( wxSYS_COLOUR_3DHILIGHT )
    SYSCLR( wxSYS_COLOUR_3DDKSHADOW )
    SYSCLR( wxSYS_COLOUR_3DLIGHT )
    SYSCLR( wxSYS_COLOUR_INFOTEXT )
    SYSCLR( wxSYS_COLOUR_INFOBK )
    SYSCLR( wxSYS_COLOUR_LISTBOX )
    SYSCLR( wxSYS_COLOUR_HOTLIGHT )
    SYSCLR( wxSYS_COLOUR_GRADIENTACTIVECAPTION )
    SYSCLR( wxSYS_COLOUR_GRADIENTINACTIVECAPTION )
    SYSCLR( wxSYS_COLOUR_MENUHILIGHT )
    SYSCLR( wxSYS_COLOUR_MENUBAR )
  }
  return wxNullColour;
}

#undef SYSCLR

wxColour wxXmlResourceHandler::GetColour( const wxString& param, const wxColour& defaultv ) {
  wxString v = GetParamValue( param );
  if( v.empty() ) {
    return defaultv;
  }
  wxColour clr;
  if( !clr.Set( v ) ) {
    clr = GetSystemColour( v );
    if( clr.Ok() ) {
      return clr;
    }
    wxLogError( _( "XRC resource: 颜色规格不正确 '%s' for property '%s'." ), v.c_str(), param.c_str() );
    return wxNullColour;
  }
  return clr;
}

wxBitmap wxXmlResourceHandler::GetBitmap( const wxString& param,
    const wxArtClient& defaultArtClient, wxSize size ) {
  wxXmlNode *bmpNode = GetParamNode( param );
  if( bmpNode ) {
    wxString sid = bmpNode->GetPropVal( wxT( "stock_id" ), wxEmptyString );
    if( !sid.empty() ) {
      wxString scl = bmpNode->GetPropVal( wxT( "stock_client" ), wxEmptyString );
      if( scl.empty() ) {
        scl = defaultArtClient;
      } else {
        scl = wxART_MAKE_CLIENT_ID_FROM_STR( scl );
      }
      wxBitmap stockArt =
        wxArtProvider::GetBitmap( wxART_MAKE_ART_ID_FROM_STR( sid ), scl, size );
      if( stockArt.Ok() ) {
        return stockArt;
      }
    }
  }
  wxString name = GetParamValue( param );
  if( name.empty() ) {
    return wxNullBitmap;
  }
  wxFSFile *fsfile = GetCurFileSystem().OpenFile( name, wxFS_READ | wxFS_SEEKABLE );
  if( fsfile == NULL ) {
    wxLogError( _( "XRC resource: 无法创建位图 '%s'." ), name.c_str() );
    return wxNullBitmap;
  }
  wxImage img( *( fsfile->GetStream() ) );
  delete fsfile;
  if( !img.Ok() ) {
    wxLogError( _( "XRC resource: 无法创建位图 '%s'." ), name.c_str() );
    return wxNullBitmap;
  }
  if( !( size == wxDefaultSize ) ) {
    img.Rescale( size.x, size.y );
  }
  return wxBitmap( img );
}

wxAnimation wxXmlResourceHandler::GetAnimation( const wxString& param ) {
  wxAnimation ani;
  wxString name = GetParamValue( param );
  if( name.empty() ) {
    return wxNullAnimation;
  }
  wxFSFile *fsfile = GetCurFileSystem().OpenFile( name, wxFS_READ | wxFS_SEEKABLE );
  if( fsfile == NULL ) {
    wxLogError( _( "XRC resource: 无法创建动画 '%s'." ), name.c_str() );
    return wxNullAnimation;
  }
  ani.Load( *( fsfile->GetStream() ) );
  delete fsfile;
  if( !ani.IsOk() ) {
    wxLogError( _( "XRC resource: 无法创建动画 '%s'." ), name.c_str() );
    return wxNullAnimation;
  }
  return ani;
}

wxIcon wxXmlResourceHandler::GetIcon( const wxString& param, const wxArtClient& defaultArtClient, wxSize size ) {
  wxIcon icon;
  icon.CopyFromBitmap( GetBitmap( param, defaultArtClient, size ) );
  return icon;
}

wxXmlNode *wxXmlResourceHandler::GetParamNode( const wxString& param ) {
  wxCHECK_MSG( m_node, NULL, wxT( "无法在初始化处理程序数据之前访问它!" ) );
  wxXmlNode *n = m_node->GetChildren();
  while( n ) {
    if( n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == param ) {
      return n;
    }
    n = n->GetNext();
  }
  return NULL;
}

bool wxXmlResourceHandler::IsOfClass( wxXmlNode *node, const wxString& classname ) {
  return node->GetPropVal( wxT( "class" ), wxEmptyString ) == classname;
}

wxString wxXmlResourceHandler::GetNodeContent( wxXmlNode *node ) {
  wxXmlNode *n = node;
  if( n == NULL ) {
    return wxEmptyString;
  }
  n = n->GetChildren();
  while( n ) {
    if( n->GetType() == wxXML_TEXT_NODE ||
        n->GetType() == wxXML_CDATA_SECTION_NODE ) {
      return n->GetContent();
    }
    n = n->GetNext();
  }
  return wxEmptyString;
}

wxString wxXmlResourceHandler::GetParamValue( const wxString& param ) {
  if( param.empty() ) {
    return GetNodeContent( m_node );
  } else
  { return GetNodeContent( GetParamNode( param ) ); }
}

wxSize wxXmlResourceHandler::GetSize( const wxString& param, wxWindow *windowToUse ) {
  wxString s = GetParamValue( param );
  if( s.empty() ) {
    s = wxT( "-1,-1" );
  }
  bool is_dlg;
  long sx, sy = 0;
  is_dlg = s[s.length() - 1] == wxT( 'd' );
  if( is_dlg ) {
    s.RemoveLast();
  }
  if( !s.BeforeFirst( wxT( ',' ) ).ToLong( &sx ) || !s.AfterLast( wxT( ',' ) ).ToLong( &sy ) ) {
    wxLogError( _( "无法分析坐标 '%s'." ), s.c_str() );
    return wxDefaultSize;
  }
  if( is_dlg ) {
    if( windowToUse ) {
      return wxDLG_UNIT( windowToUse, wxSize( sx, sy ) );
    } else if( m_parentAsWindow ) {
      return wxDLG_UNIT( m_parentAsWindow, wxSize( sx, sy ) );
    } else {
      wxLogError( _( "无法转换对话框单位：对话框未知." ) );
      return wxDefaultSize;
    }
  }
  return wxSize( sx, sy );
}

wxPoint wxXmlResourceHandler::GetPosition( const wxString& param ) {
  wxSize sz = GetSize( param );
  return wxPoint( sz.x, sz.y );
}

wxCoord wxXmlResourceHandler::GetDimension( const wxString& param, wxCoord defaultv, wxWindow *windowToUse ) {
  wxString s = GetParamValue( param );
  if( s.empty() ) {
    return defaultv;
  }
  bool is_dlg;
  long sx;
  is_dlg = s[s.length() - 1] == wxT( 'd' );
  if( is_dlg ) {
    s.RemoveLast();
  }
  if( !s.ToLong( &sx ) ) {
    wxLogError( _( "无法分析维度 '%s'." ), s.c_str() );
    return defaultv;
  }
  if( is_dlg ) {
    if( windowToUse ) {
      return wxDLG_UNIT( windowToUse, wxSize( sx, 0 ) ).x;
    } else if( m_parentAsWindow ) {
      return wxDLG_UNIT( m_parentAsWindow, wxSize( sx, 0 ) ).x;
    } else {
      wxLogError( _( "无法转换对话框单位：对话框未知." ) );
      return defaultv;
    }
  }
  return sx;
}

static wxFont GetSystemFont( const wxString& name ) {
  if( !name.empty() ) {
#define SYSFNT(fnt) \
  if (name == _T(#fnt)) return wxSystemSettings::GetFont(fnt);
    SYSFNT( wxSYS_OEM_FIXED_FONT )
    SYSFNT( wxSYS_ANSI_FIXED_FONT )
    SYSFNT( wxSYS_ANSI_VAR_FONT )
    SYSFNT( wxSYS_SYSTEM_FONT )
    SYSFNT( wxSYS_DEVICE_DEFAULT_FONT )
    SYSFNT( wxSYS_DEFAULT_PALETTE )
    SYSFNT( wxSYS_SYSTEM_FIXED_FONT )
    SYSFNT( wxSYS_DEFAULT_GUI_FONT )
#undef SYSFNT
  }
  return wxNullFont;
}

wxFont wxXmlResourceHandler::GetFont( const wxString& param ) {
  wxXmlNode *font_node = GetParamNode( param );
  if( font_node == NULL ) {
    wxLogError( _( "找不到字体节点 '%s'." ), param.c_str() );
    return wxNullFont;
  }
  wxXmlNode *oldnode = m_node;
  m_node = font_node;
  int isize = -1;
  bool hasSize = HasParam( wxT( "size" ) );
  if( hasSize ) {
    isize = GetLong( wxT( "size" ), -1 );
  }
  int istyle = wxNORMAL;
  bool hasStyle = HasParam( wxT( "style" ) );
  if( hasStyle ) {
    wxString style = GetParamValue( wxT( "style" ) );
    if( style == wxT( "italic" ) ) {
      istyle = wxITALIC;
    } else if( style == wxT( "slant" ) ) {
      istyle = wxSLANT;
    }
  }
  int iweight = wxNORMAL;
  bool hasWeight = HasParam( wxT( "weight" ) );
  if( hasWeight ) {
    wxString weight = GetParamValue( wxT( "weight" ) );
    if( weight == wxT( "bold" ) ) {
      iweight = wxBOLD;
    } else if( weight == wxT( "light" ) ) {
      iweight = wxLIGHT;
    }
  }
  bool hasUnderlined = HasParam( wxT( "underlined" ) );
  bool underlined = hasUnderlined ? GetBool( wxT( "underlined" ), false ) : false;
  int ifamily = wxDEFAULT;
  bool hasFamily = HasParam( wxT( "family" ) );
  if( hasFamily ) {
    wxString family = GetParamValue( wxT( "family" ) );
    if( family == wxT( "decorative" ) ) {
      ifamily = wxDECORATIVE;
    } else if( family == wxT( "roman" ) ) {
      ifamily = wxROMAN;
    } else if( family == wxT( "script" ) ) {
      ifamily = wxSCRIPT;
    } else if( family == wxT( "swiss" ) ) {
      ifamily = wxSWISS;
    } else if( family == wxT( "modern" ) ) {
      ifamily = wxMODERN;
    } else if( family == wxT( "teletype" ) ) {
      ifamily = wxTELETYPE;
    }
  }
  wxString facename;
  bool hasFacename = HasParam( wxT( "face" ) );
  if( hasFacename ) {
    wxString faces = GetParamValue( wxT( "face" ) );
    wxArrayString facenames( wxFontEnumerator::GetFacenames() );
    wxStringTokenizer tk( faces, wxT( "," ) );
    while( tk.HasMoreTokens() ) {
      int index = facenames.Index( tk.GetNextToken(), false );
      if( index != wxNOT_FOUND ) {
        facename = facenames[index];
        break;
      }
    }
  }
  wxFontEncoding enc = wxFONTENCODING_DEFAULT;
  bool hasEncoding = HasParam( wxT( "encoding" ) );
  if( hasEncoding ) {
    wxString encoding = GetParamValue( wxT( "encoding" ) );
    wxFontMapper mapper;
    if( !encoding.empty() ) {
      enc = mapper.CharsetToEncoding( encoding );
    }
    if( enc == wxFONTENCODING_SYSTEM ) {
      enc = wxFONTENCODING_DEFAULT;
    }
  }
  wxFont font = GetSystemFont( GetParamValue( wxT( "sysfont" ) ) );
  if( font.Ok() ) {
    if( hasSize && isize != -1 ) {
      font.SetPointSize( isize );
    } else if( HasParam( wxT( "relativesize" ) ) ) {
      font.SetPointSize( int( font.GetPointSize() * GetFloat( wxT( "relativesize" ) ) ) );
    }
    if( hasStyle ) {
      font.SetStyle( istyle );
    }
    if( hasWeight ) {
      font.SetWeight( iweight );
    }
    if( hasUnderlined ) {
      font.SetUnderlined( underlined );
    }
    if( hasFamily ) {
      font.SetFamily( ifamily );
    }
    if( hasFacename ) {
      font.SetFaceName( facename );
    }
    if( hasEncoding ) {
      font.SetDefaultEncoding( enc );
    }
  } else {
    font = wxFont( isize == -1 ? wxNORMAL_FONT->GetPointSize() : isize, ifamily, istyle, iweight, underlined, facename, enc );
  }
  m_node = oldnode;
  return font;
}


void wxXmlResourceHandler::SetupWindow( wxWindow *wnd ) {
  if( HasParam( wxT( "exstyle" ) ) ) {
    wnd->SetExtraStyle( wnd->GetExtraStyle() | GetStyle( wxT( "exstyle" ) ) );
  }
  if( HasParam( wxT( "bg" ) ) ) {
    wnd->SetBackgroundColour( GetColour( wxT( "bg" ) ) );
  }
  if( HasParam( wxT( "fg" ) ) ) {
    wnd->SetForegroundColour( GetColour( wxT( "fg" ) ) );
  }
  if( GetBool( wxT( "enabled" ), 1 ) == 0 ) {
    wnd->Enable( false );
  }
  if( GetBool( wxT( "focused" ), 0 ) == 1 ) {
    wnd->SetFocus();
  }
  if( GetBool( wxT( "hidden" ), 0 ) == 1 ) {
    wnd->Show( false );
  }
  if( HasParam( wxT( "tooltip" ) ) ) {
    wnd->SetToolTip( GetText( wxT( "tooltip" ) ) );
  }
  if( HasParam( wxT( "font" ) ) ) {
    wnd->SetFont( GetFont() );
  }
  if( HasParam( wxT( "help" ) ) ) {
    wnd->SetHelpText( GetText( wxT( "help" ) ) );
  }
}

void wxXmlResourceHandler::CreateChildren( wxObject *parent, bool this_hnd_only ) {
  wxXmlNode *n = m_node->GetChildren();
  while( n ) {
    if( n->GetType() == wxXML_ELEMENT_NODE &&
        ( n->GetName() == wxT( "object" ) || n->GetName() == wxT( "object_ref" ) ) ) {
      m_resource->CreateResFromNode( n, parent, NULL, this_hnd_only ? this : NULL );
    }
    n = n->GetNext();
  }
}

void wxXmlResourceHandler::CreateChildrenPrivately( wxObject *parent, wxXmlNode *rootnode ) {
  wxXmlNode *root;
  if( rootnode == NULL ) {
    root = m_node;
  } else {
    root = rootnode;
  }
  wxXmlNode *n = root->GetChildren();
  while( n ) {
    if( n->GetType() == wxXML_ELEMENT_NODE && CanHandle( n ) ) {
      CreateResource( n, parent, NULL );
    }
    n = n->GetNext();
  }
}

#define XRCID_TABLE_SIZE     1024

struct XRCID_record {
  int id;
  wxChar *key;
  XRCID_record *next;
};

static XRCID_record *XRCID_Records[XRCID_TABLE_SIZE] = {NULL};

static int XRCID_Lookup( const wxChar *str_id, int value_if_not_found = wxID_NONE ) {
  unsigned int index = 0;
  for( const wxChar *c = str_id; *c != wxT( '\0' ); c++ ) {
    index += ( unsigned int ) * c;
  }
  index %= XRCID_TABLE_SIZE;
  XRCID_record *oldrec = NULL;
  for( XRCID_record *rec = XRCID_Records[index]; rec; rec = rec->next ) {
    if( wxStrcmp( rec->key, str_id ) == 0 ) {
      return rec->id;
    }
    oldrec = rec;
  }
  XRCID_record **rec_var = ( oldrec == NULL ) ? &XRCID_Records[index] : &oldrec->next;
  *rec_var = new XRCID_record;
  ( *rec_var )->key = wxStrdup( str_id );
  ( *rec_var )->next = NULL;
  wxChar *end;
  if( value_if_not_found != wxID_NONE ) {
    ( *rec_var )->id = value_if_not_found;
  } else {
    int asint = wxStrtol( str_id, &end, 10 );
    if( *str_id && *end == 0 ) {
      ( *rec_var )->id = asint;
    } else {
      ( *rec_var )->id = wxNewId();
    }
  }
  return ( *rec_var )->id;
}

static void AddStdXRCID_Records();

int wxXmlResource::GetXRCID( const wxChar *str_id, int value_if_not_found ) {
  static bool s_stdIDsAdded = false;
  if( !s_stdIDsAdded ) {
    s_stdIDsAdded = true;
    AddStdXRCID_Records();
  }
  return XRCID_Lookup( str_id, value_if_not_found );
}


static void CleanXRCID_Record( XRCID_record *rec ) {
  if( rec ) {
    CleanXRCID_Record( rec->next );
    free( rec->key );
    delete rec;
  }
}

static void CleanXRCID_Records() {
  for( int i = 0; i < XRCID_TABLE_SIZE; i++ ) {
    CleanXRCID_Record( XRCID_Records[i] );
    XRCID_Records[i] = NULL;
  }
}

static void AddStdXRCID_Records() {
#define stdID(id) XRCID_Lookup(wxT(#id), id)
  stdID( -1 );
  stdID( wxID_ANY );
  stdID( wxID_SEPARATOR );
  stdID( wxID_OPEN );
  stdID( wxID_CLOSE );
  stdID( wxID_NEW );
  stdID( wxID_SAVE );
  stdID( wxID_SAVEAS );
  stdID( wxID_REVERT );
  stdID( wxID_EXIT );
  stdID( wxID_UNDO );
  stdID( wxID_REDO );
  stdID( wxID_HELP );
  stdID( wxID_PRINT );
  stdID( wxID_PRINT_SETUP );
  stdID( wxID_PAGE_SETUP );
  stdID( wxID_PREVIEW );
  stdID( wxID_ABOUT );
  stdID( wxID_HELP_CONTENTS );
  stdID( wxID_HELP_COMMANDS );
  stdID( wxID_HELP_PROCEDURES );
  stdID( wxID_HELP_CONTEXT );
  stdID( wxID_CLOSE_ALL );
  stdID( wxID_PREFERENCES );
  stdID( wxID_EDIT );
  stdID( wxID_CUT );
  stdID( wxID_COPY );
  stdID( wxID_PASTE );
  stdID( wxID_CLEAR );
  stdID( wxID_FIND );
  stdID( wxID_DUPLICATE );
  stdID( wxID_SELECTALL );
  stdID( wxID_DELETE );
  stdID( wxID_REPLACE );
  stdID( wxID_REPLACE_ALL );
  stdID( wxID_PROPERTIES );
  stdID( wxID_VIEW_DETAILS );
  stdID( wxID_VIEW_LARGEICONS );
  stdID( wxID_VIEW_SMALLICONS );
  stdID( wxID_VIEW_LIST );
  stdID( wxID_VIEW_SORTDATE );
  stdID( wxID_VIEW_SORTNAME );
  stdID( wxID_VIEW_SORTSIZE );
  stdID( wxID_VIEW_SORTTYPE );
  stdID( wxID_FILE1 );
  stdID( wxID_FILE2 );
  stdID( wxID_FILE3 );
  stdID( wxID_FILE4 );
  stdID( wxID_FILE5 );
  stdID( wxID_FILE6 );
  stdID( wxID_FILE7 );
  stdID( wxID_FILE8 );
  stdID( wxID_FILE9 );
  stdID( wxID_OK );
  stdID( wxID_CANCEL );
  stdID( wxID_APPLY );
  stdID( wxID_YES );
  stdID( wxID_NO );
  stdID( wxID_STATIC );
  stdID( wxID_FORWARD );
  stdID( wxID_BACKWARD );
  stdID( wxID_DEFAULT );
  stdID( wxID_MORE );
  stdID( wxID_SETUP );
  stdID( wxID_RESET );
  stdID( wxID_CONTEXT_HELP );
  stdID( wxID_YESTOALL );
  stdID( wxID_NOTOALL );
  stdID( wxID_ABORT );
  stdID( wxID_RETRY );
  stdID( wxID_IGNORE );
  stdID( wxID_ADD );
  stdID( wxID_REMOVE );
  stdID( wxID_UP );
  stdID( wxID_DOWN );
  stdID( wxID_HOME );
  stdID( wxID_REFRESH );
  stdID( wxID_STOP );
  stdID( wxID_INDEX );
  stdID( wxID_BOLD );
  stdID( wxID_ITALIC );
  stdID( wxID_JUSTIFY_CENTER );
  stdID( wxID_JUSTIFY_FILL );
  stdID( wxID_JUSTIFY_RIGHT );
  stdID( wxID_JUSTIFY_LEFT );
  stdID( wxID_UNDERLINE );
  stdID( wxID_INDENT );
  stdID( wxID_UNINDENT );
  stdID( wxID_ZOOM_100 );
  stdID( wxID_ZOOM_FIT );
  stdID( wxID_ZOOM_IN );
  stdID( wxID_ZOOM_OUT );
  stdID( wxID_UNDELETE );
  stdID( wxID_REVERT_TO_SAVED );
  stdID( wxID_SYSTEM_MENU );
  stdID( wxID_CLOSE_FRAME );
  stdID( wxID_MOVE_FRAME );
  stdID( wxID_RESIZE_FRAME );
  stdID( wxID_MAXIMIZE_FRAME );
  stdID( wxID_ICONIZE_FRAME );
  stdID( wxID_RESTORE_FRAME );
#undef stdID
}

class wxXmlResourceModule: public wxModule {
    DECLARE_DYNAMIC_CLASS( wxXmlResourceModule )
  public:
    wxXmlResourceModule() {}
    bool OnInit() {
      wxXmlResource::AddSubclassFactory( new wxXmlSubclassFactoryCXX );
      return true;
    }
    void OnExit() {
      delete wxXmlResource::Set( NULL );
      if( wxXmlResource::ms_subclassFactories ) {
        WX_CLEAR_LIST( wxXmlSubclassFactoriesList, *wxXmlResource::ms_subclassFactories );
      }
      wxDELETE( wxXmlResource::ms_subclassFactories );
      CleanXRCID_Records();
    }
};

IMPLEMENT_DYNAMIC_CLASS( wxXmlResourceModule, wxModule )
void wxXmlInitResourceModule() {
  wxModule* module = new wxXmlResourceModule;
  module->Init();
  wxModule::RegisterModule( module );
}
